/*
 ------------------------------------------------------------------------------
 Copyright (C) 2013 Eternal Games.

 This file is part of the EternalTech source code.

 The EternalTech source code is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or (at your
 option) any later version.

 The EternalTech source code is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 more details.

 You should have received a copy of the GNU General Public License along with
 the EternalTech source code; if not, write to the Free Software Foundation, Inc.,
 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 ------------------------------------------------------------------------------
*/


//
// s_filter.cpp - low-pass filters
//


#include "s_local.h"


/*
 ==================
 S_ObstructionFilter

 The amount of sound obstruction will be proportional to the distance the sound
 must travel through solid surfaces in the path from emitter to listener (not
 including entities, just the world).

 To simplify the process, the thickness and composition of the surfaces and any
 empty spaces between multiple obstacles are ignored. Diffraction of the sound
 waves is not taken into account either.
 ==================
*/
void S_ObstructionFilter (const glqVec3 &srcOrigin, const glqVec3 &dstOrigin, float distance, float minDistance, float maxDistance, filterParms_t *dryFilter){

	trace_t	srcTrace, dstTrace;
	float	attenuation, filtering;
	float	fraction;

	if (distance <= minDistance || distance >= maxDistance)
		return;

	// Trace to find obstacles
	CM_Trace(srcOrigin, dstOrigin, bounds_zero, MASK_SOLID, NULL, vec3_origin, mat3_identity, &srcTrace);

	if (srcTrace.allInside || srcTrace.startInside || srcTrace.fraction == 1.0f)
		return;

	CM_Trace(dstOrigin, srcOrigin, bounds_zero, MASK_SOLID, NULL, vec3_origin, mat3_identity, &dstTrace);

	if (dstTrace.allInside || dstTrace.startInside || dstTrace.fraction == 1.0f)
		return;

	distance = glqMath::ClampFloat(srcTrace.point.Distance(dstTrace.point), minDistance, maxDistance);

	fraction = (distance - minDistance) / (maxDistance - minDistance);

	// Compute obstruction
	attenuation = 1.0f - (0.25f * fraction * s_obstructionScale->floatValue);
	attenuation = Max(attenuation, 0.0f);

	filtering = 1.0f - (0.75f * fraction * s_obstructionScale->floatValue);
	filtering = Max(filtering, 0.0f);

	// Apply attenuation and filtering to the direct path
	dryFilter->gain *= attenuation;
	dryFilter->gainHF *= filtering;
}


/*
 ==============================================================================

 FRAMEWORK

 ==============================================================================
*/


/*
 ==================
 S_UpdateFilters
 ==================
*/
void S_UpdateFilters (int index, filterParms_t *dryFilter, filterParms_t *wetFilter){

	if (!snd.filter.enabled)
		return;

	if (index < 0 || index >= MAX_FILTERS)
		Com_Error(false, "S_UpdateFilters: index out of range");

	qalFilterf(snd.filter.dryFilterId[index], AL_LOWPASS_GAIN, dryFilter->gain);
	qalFilterf(snd.filter.dryFilterId[index], AL_LOWPASS_GAINHF, dryFilter->gainHF);

	qalFilterf(snd.filter.wetFilterId[index], AL_LOWPASS_GAIN, wetFilter->gain);
	qalFilterf(snd.filter.wetFilterId[index], AL_LOWPASS_GAINHF, wetFilter->gainHF);
}


/*
 ==============================================================================

 INITIALIZATION & SHUTDOWN

 ==============================================================================
*/


/*
 ==================
 S_InitFilters
 ==================
*/
void S_InitFilters (){

	int		i;

	// Enable low-pass filters if possible
	if (!s_lowPassFilters->integerValue || !alConfig.efxAvailable)
		return;

	snd.filter.enabled = true;

	// Create and set up the filters
	for (i = 0; i < MAX_FILTERS; i++){
		qalGenFilters(1, &snd.filter.dryFilterId[i]);
		qalFilteri(snd.filter.dryFilterId[i], AL_FILTER_TYPE, AL_FILTER_LOWPASS);
		qalFilterf(snd.filter.dryFilterId[i], AL_LOWPASS_GAIN, 1.0f);
		qalFilterf(snd.filter.dryFilterId[i], AL_LOWPASS_GAINHF, 1.0f);

		qalGenFilters(1, &snd.filter.wetFilterId[i]);
		qalFilteri(snd.filter.wetFilterId[i], AL_FILTER_TYPE, AL_FILTER_LOWPASS);
		qalFilterf(snd.filter.wetFilterId[i], AL_LOWPASS_GAIN, 1.0f);
		qalFilterf(snd.filter.wetFilterId[i], AL_LOWPASS_GAINHF, 1.0f);
	}

	// Disable low-pass filters if an error occurred
	if (qalGetError() != AL_NO_ERROR){
		for (i = 0; i < MAX_FILTERS; i++){
			qalFilteri(snd.filter.dryFilterId[i], AL_FILTER_TYPE, 0);
			qalDeleteFilters(1, &snd.filter.dryFilterId[i]);

			qalFilteri(snd.filter.wetFilterId[i], AL_FILTER_TYPE, 0);
			qalDeleteFilters(1, &snd.filter.wetFilterId[i]);
		}

		qalGetError();

		// Clear the variables
		snd.filter.enabled = false;

		Mem_Fill(snd.filter.dryFilterId, 0, sizeof(snd.filter.dryFilterId));
		Mem_Fill(snd.filter.wetFilterId, 0, sizeof(snd.filter.wetFilterId));
	}
}

/*
 ==================
 S_ShutdownFilters
 ==================
*/
void S_ShutdownFilters (){

	int		i;

	if (!snd.filter.enabled)
		return;

	// Delete all the filters
	for (i = 0; i < MAX_FILTERS; i++){
		qalFilteri(snd.filter.dryFilterId[i], AL_FILTER_TYPE, 0);
		qalDeleteFilters(1, &snd.filter.dryFilterId[i]);

		qalFilteri(snd.filter.wetFilterId[i], AL_FILTER_TYPE, 0);
		qalDeleteFilters(1, &snd.filter.wetFilterId[i]);
	}
}